package com.njitzx.service.impl;

import com.alibaba.druid.sql.visitor.functions.If;
import com.njitzx.constant.MenuConstant;
import com.njitzx.entity.po.Permission;
import com.njitzx.entity.po.Role;
import com.njitzx.entity.po.User;
import com.njitzx.entity.vo.PermVO;
import com.njitzx.entity.vo.RefreshPerVO;
import com.njitzx.entity.vo.RoleVO;
import com.njitzx.exception.RoleException;
import com.njitzx.mapper.PermissionMapper;
import com.njitzx.mapper.RoleMapper;
import com.njitzx.mapper.UserMapper;
import com.njitzx.service.RoleService;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.sound.midi.Soundbank;


/**
 * <p>
 * 角色表 服务实现类
 * </p>
 *
 * @author 周鑫
 * @since 2024-08-20
 */
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class RoleServiceImpl implements RoleService {

    private final UserMapper userMapper;
    private final RoleMapper roleMapper;
    private final PermissionMapper permissionMapper;
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 角色表分页查询
     *
     * @param pageNum
     * @param pageSize
     * @param role
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    @Override
    public PageInfo<Role> findPage(Integer pageNum, Integer pageSize, Role role) {
        PageHelper.startPage(pageNum, pageSize);
        List<Role> roleList = roleMapper.select(role);
        return new PageInfo<>(roleList);
    }

    /**
     * 根据主键查询角色表
     *
     * @param roleId
     * @return
     */
    @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
    @Override
    public Role findById(Serializable roleId) {
        return roleMapper.selectById(roleId);
    }

    /**
     * 修改角色表
     *
     * @param role
     */
    @Override
    public void modify(Role role) {
        roleMapper.update(role);
    }

    /**
     * 根据主键删除角色表
     *
     * @param roleId
     */
    @Override
    public void removeById(Serializable roleId) {
        //查询这个角色下面有没有用户
        List<User> users = userMapper.getUsrByRoleId(roleId);
        if (users.size() > 0) {
            throw new RoleException("该角色下面有用户，不能删除");
        }
        roleMapper.deleteById(roleId);
    }

    @Override
    public List<Role> findAll() {
        return roleMapper.queryAll();
    }

    @Override
    public void addRole(String roleName, String roleDesc) {
        Role role = new Role();
        role.setRoleName(roleName);
        if (roleDesc != null) {
            role.setRoleDesc(roleDesc);
        }
        roleMapper.insert(role);
    }

    @Override
    public void grandRole(Integer roleId, String ids) {

        roleMapper.grand(ids, roleId);

        //执行到这一步  删除redis中所有缓存的菜单
        Set<String> keys = redisTemplate.keys(MenuConstant.MENU_PREFIX + "*");  //删除所有的权限 会更新权限的
        redisTemplate.delete(keys);
    }

    /**
     * 查询角色列表
     * <p>
     * 本方法通过角色映射器查询所有角色，并为每个角色收集其相关的权限信息，封装为RoleVO对象
     * 它首先从数据库中检索Role实体，然后为每个角色获取其权限（Permission）信息，最后将这些信息封装到RoleVO对象中
     * 这里的主要逻辑是通过流处理（Stream API）遍历角色列表，为每个角色组装其权限树
     *
     * @return 返回一个包含所有角色及其权限信息的RoleVO对象列表
     */
    @Override
    public List<RoleVO> findrRoeList() {
        // 使用角色映射器查询所有角色实体
        List<RoleVO> collect = roleMapper.select(new Role())
                // 将角色实体流转换为RoleVO对象流，并为每个角色收集权限信息
                .stream().map(role -> {
                    // 将角色的权限ID字符串分割为数组
                    String[] ids = role.getPsIds().split(",");
                    // 根据权限ID数组获取权限列表
                    List<Permission> permissons = permissionMapper.getPermisson(ids); //这是一个多表查询
                    // 过滤并组装角色的一级权限及其子权限
                    List<PermVO> children = permissons.stream().filter(perm -> perm.getPsPid() == 0)
                            .map(item -> {
                                // 创建PermVO对象并设置属性
                                PermVO child = getPermVO(item);
                                // 递归查找并设置子权限
                                findChild(child, permissons);
                                return child;
                            }).collect(Collectors.toList());
                    // 封装RoleVO对象并设置其权限信息
                    RoleVO roleVO = new RoleVO();
                    // 将Role实体的属性复制到RoleVO对象
                    BeanUtils.copyProperties(role, roleVO);
                    // 设置RoleVO对象的权限列表
                    roleVO.setChildren(children);
                    return roleVO;
                }).collect(Collectors.toList());
        return collect;
    }


    /**
     * @param roleId  角色id
     * @param rightId 权限id
     * @return
     */
    @Override
    public List<PermVO> deleteRolePermission(Integer roleId, Integer rightId) {
        //1.拿到需要删除权限和所有子权限
        List<Integer> needDelete = new ArrayList<>();
        //获得当前要删除节点得子节点
        needDelete.add(rightId);
        findChildren(rightId, needDelete);//找到所有孩子得id

        //拿到当前角色的所有权限
        List<Integer> currentPer = Arrays.stream(roleMapper.selectById(roleId).getPsIds()
                        .split(",")).map(item -> Integer.valueOf(item))
                .collect(Collectors.toList());

        //filter操作两个集合 拿到剩下的所有权限
        String remaining = currentPer.stream().filter(item -> !needDelete.contains(item))
                .map(String::valueOf).collect(Collectors.joining(","));

        //更新角色权限
        Role role = new Role();
        role.setRoleId(roleId);
        role.setPsIds(remaining);
        roleMapper.update(role);

        //删除当前redis所有的菜单全单，为什么要删除呢，因为修改了一个角色可能会影响多个用户
        redisTemplate.delete(redisTemplate.keys(MenuConstant.MENU_PREFIX + "*"));

        //重新获取当前的菜单
        return refreshMenu(roleId);
    }

    public List<PermVO> refreshMenu(Integer roleId) {
        //拿到roleid
        String[] split = roleMapper.selectById(roleId).getPsIds().split(",");

        List<Permission> permissions = permissionMapper.getPermisson(split);

         //拿到所有的一级权限 然后递归子节点的权限
        List<PermVO> collect = permissions.stream().filter(item -> item.getPsPid() == 0) //拿到父接待你 对父接待你进行递归哦
                .map(item -> {
                    return recursionChildren(item, permissions);
                }).collect(Collectors.toList());
        return collect;
    }

    private PermVO recursionChildren(Permission permission, List<Permission> permissions) {
        PermVO permVO1 = getPermVO(permission);  //变为vo
        List<PermVO> children = permissions.stream()
                .filter(item -> item.getPsPid().equals(permission.getPsId()))  //Intger是有缓存的，
                .map(item -> {
                    return recursionChildren(item, permissions);
                }).collect(Collectors.toList());
        permVO1.setChildren(children);
        return permVO1;
    }

    public void findChildren(Integer parentId, List<Integer> permissions) {
        List<Integer> list = permissionMapper.selectByIdPSId(parentId);
        for (Integer integer : list) {
            findChildren(integer, permissions);
            permissions.add(integer);
        }
    }

    /**
     * 递归查找权限的子权限
     *
     * @param permVO     当前权限信息的封装对象
     * @param permissons 所有权限信息的列表，用于递归查找子权限
     */
    private void findChild(PermVO permVO, List<Permission> permissons) {
        // 根据当前权限信息，从所有权限列表中筛选出该权限的所有直接子权限
        List<PermVO> children = permissons.stream().filter(item ->
                // 确保子权限的父权限ID与当前权限ID相等
                item.getPsPid().equals(permVO.getId())
        ).map(subitem -> {
            // 对每个匹配到的子权限，创建其对应的PermVO对象
            PermVO child = this.getPermVO(subitem);
            // 递归调用findChild方法，继续查找当前子权限的子权限
            findChild(child, permissons);
            return child;
        }).collect(Collectors.toList());
        // 将收集到的所有子权限设置给当前权限信息的封装对象
        permVO.setChildren(children);
    }

    private PermVO getPermVO(Permission item) {
        PermVO permVO = new PermVO();
        permVO.setId(item.getPsId());
        permVO.setAuthName(item.getPsName());
        permVO.setLevel(item.getPsLevel());
        permVO.setPath(item.getPsApiPath());
        return permVO;
    }

    public RefreshPerVO getRefreshPerVO(Permission item) {
        RefreshPerVO refreshPerVO = new RefreshPerVO();
        refreshPerVO.setId(item.getPsId());
        refreshPerVO.setAuthName(item.getPsName());
        refreshPerVO.setPath(item.getPsApiPath());
        return refreshPerVO;
    }

}
